<script>
import FilterItem from "./FilterItem";
import FilterFooter from "./FilterFooter";
import SxFilterFieldSetting from "@Components/Search/FilterFieldSetting";
import DateRange from "./render/dateRange";
import Address from "./render/Address";
import cloneDeep from "lodash/cloneDeep";
import localStore from "@Components/assets/js/utils/localStore";

const InputType = "Input";
const SelectType = "Select";
const FlagType = "Flag";
const MultipleSelectType = "MultipleSelect";
const DateType = "Date";
const DateRangeType = "DateRange";
const CascaderType = "Cascader";
const CheckboxType = "Checkbox";
const AddressType = "Address";

export default {
  /**
   * @typedef { object } FormFields
   * @property { string| string[] } field
   * @property { string } title
   * @property { any } initialValue
   * @property { InputType | SelectType | FlagType | MultipleSelectType | DateType | DateRangeType | CascaderType | CheckboxType } type
   * @property { {
   *      listKey?: string;
   *      [key: string]: any;
   *    } } options
   */
  props: {
    selectOptionsList: {
      type: Object,
      default() {
        return {};
      }
    },
    /**
     * @type {Array<FormFields>}
     */
    formFields: {
      type: Array,
      default() {
        return [];
      }
    },
    id: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      defaultDateFormat: "yyyy-MM-dd HH:mm:ss",
      formData: {},
      formFieldsMap: {},
      settingVisable: false,
      settingData: [],
      actualFields: []
    };
  },
  beforeMount() {
    this.generateFormFieldMap();
    this.generateFields();
    this.generateFilterSettingData();
    this.initFormData();
  },
  methods: {
    initFormData() {
      /** @type { {actualFields: Array<FormFields>} } */
      const { actualFields } = this;
      actualFields.forEach(item => {
        switch (item.type) {
          case DateRangeType: {
            const fields = item.field;
            const { initialValue = [] } = item;
            const [initialVal1, initialVal2] = initialValue;

            if (!Array.isArray(fields)) {
              throw new Error("以数组的形式传入`field`，['start', 'end']");
            }
            this.$set(this.formData, fields[0], initialVal1 ?? "");
            this.$set(this.formData, fields[1], initialVal2 ?? "");
            break;
          }

          case MultipleSelectType: {
            const initValue = item.initialValue ?? [];
            this.$set(this.formData, item.field, initValue);
            break;
          }

          case CheckboxType: {
            const initValue = item.initialValue ?? false;
            this.$set(this.formData, item.field, initValue);
            break;
          }
          case AddressType: {
            const fields = item.field;
            const initialVals = item.initialValue || [];
            fields.forEach((key, index) => {
              this.$set(this.formData, key, initialVals?.[index] || "");
            });
            break;
          }
          default: {
            let initValue = item.initialValue ?? "";
            if (item.initialValue == null) initValue = item.initialValue;
            this.$set(this.formData, item.field, initValue);
            break;
          }
        }
      });
    },
    handleSelectOptions(listKey) {
      return this.selectOptionsList[listKey]?.list || [];
    },
    // 渲染 Input
    renderInput(item) {
      const { formData } = this.$data;
      const placeholder = item?.options?.placeholder ?? `请输入${item.title}`;

      return (
        <el-input
          size={this.$store.getters.size}
          v-model={formData[item.field]}
          placeholder={placeholder}
          {...{
            props: item.options
          }}
          clearable
          onChange={this.search}
        />
      );
    },
    // 渲染 Checkbox
    renderCheckbox(item) {
      const { formData } = this.$data;
      return (
        <el-checkbox
          class="SxFilterSearch__checkbox"
          size={this.$store.getters.size}
          label={item.title}
          v-model={formData[item.field]}
          {...{
            props: {
              placeholder: `请输入${item.title}`,
              ...item.options
            }
          }}
          onChange={this.search}
        />
      );
    },
    // 渲染 Select
    renderSelect(item) {
      const { formData } = this.$data;
      const options = this.handleSelectOptions(item.options.listKey);
      const isMultiple = item.type === MultipleSelectType;
      return (
        <el-select
          style={{ width: item.options && `${item.options.width}px` }}
          v-model={formData[item.field]}
          collapse-tags={isMultiple}
          multiple={isMultiple}
          size={this.$store.getters.size}
          filterable
          autocomplete="none"
          clearable
          {...{
            props: {
              placeholder: `请选择${item.title}`,
              ...item.options
            }
          }}
          onChange={this.search}
        >
          {options.map(option => this.renderSelectOption(option, item))}
        </el-select>
      );
    },
    // 渲染 Select option
    renderSelectOption(option, item) {
      return (
        <el-option key={option.value} label={option.label} value={option.value}>
          {item.type === FlagType && (
            <i class="el-icon-s-flag" style={{ color: option.color }} />
          )}
        </el-option>
      );
    },
    // 渲染 Date Picker
    renderDatePicker(item) {
      const { formData, defaultDateFormat } = this.$data;
      const dateFormat = item.format ?? defaultDateFormat;
      return (
        <el-date-picker
          v-model={formData[item.field]}
          size={this.$store.getters.size}
          type="datetime"
          clearable
          format={dateFormat}
          value-format={dateFormat}
          {...{
            props: {
              placeholder: `请选择${item.title}`,
              ...item.options
            }
          }}
          onChange={this.search}
        />
      );
    },
    // 渲染 Date Range
    renderDateRange(item) {
      const { formData, defaultDateFormat } = this.$data;
      const dateFormat = item.format ?? defaultDateFormat;
      const fields = item.field;
      if (fields.length !== 2) {
        return null;
      }
      const [startField, endField] = fields;
      const publicAttrs = {
        size: "mini",
        type: "datetime",
        clearable: true,
        format: dateFormat,
        valueFormat: dateFormat,
        ...item.options
      };
      return (
        <DateRange
          attrs={publicAttrs}
          start={formData[startField]}
          end={formData[endField]}
          on={{
            "update:start": e => (formData[startField] = e),
            "update:end": e => (formData[endField] = e),
            pick: this.search
          }}
        />
      );
    },
    // 渲染 Cascader
    renderCascader(item) {
      const { formData } = this.$data;
      const options = this.handleSelectOptions(item.options.listKey);

      const handleCascaderChange = (item, node) => {
        this.$set(formData, item.field, node.path);
        this.search();
      };
      return (
        <el-cascader
          size={this.$store.getters.size}
          options={options}
          v-model={formData[item.field]}
          clearable
          props={{
            placeholder: `请选择${item.title}`,
            ...item.options
          }}
          onChange={this.search}
          scopedSlots={{
            default: ({ node, data }) => (
              <div
                onClick={() => {
                  handleCascaderChange(item, node);
                }}
              >
                <span>{data.label}</span>
              </div>
            )
          }}
        />
      );
    },
    //渲染省市区
    renderAddress(item) {
      const { formData } = this.$data;
      const fields = item.field;
      if (!Array.isArray(fields)) {
        throw new Error(
          "以数组的形式传入`field`，['province', 'city', 'area']"
        );
      }
      if (fields.length === 0 || fields.length > 3) {
        throw new Error("`field`字段元素最少为1个，最多为3个");
      }
      const syncData = data => {
        fields.forEach((key, index) => {
          formData[key] = data[index];
        });
      };
      const handleChange = data => {
        syncData(data);
        this.search();
      };

      const handleBlur = data => {
        syncData(data);
      };

      const handleClear = () => {
        syncData(fields.map(() => ""));
      };
      return (
        <Address
          field={fields}
          initialValue={item.initialValue}
          title={item.title}
          onChange={handleChange}
          onBlur={handleBlur}
          onClear={handleClear}
          options={item.options}
        />
      );
    },
    // 渲染 表单项
    /**
     * @param {FormFields} formItem
     */
    renderFormItem(formItem) {
      let renderMethod;
      switch (formItem.type) {
        case SelectType:
        case FlagType:
        case MultipleSelectType:
          renderMethod = this.renderSelect;
          break;
        case DateType:
          renderMethod = this.renderDatePicker;
          break;
        case DateRangeType:
          renderMethod = this.renderDateRange;
          break;
        case CascaderType:
          renderMethod = this.renderCascader;
          break;
        case AddressType:
          renderMethod = this.renderAddress;
          break;
        case CheckboxType:
          renderMethod = this.renderCheckbox;
          break;
        case InputType:
        default:
          renderMethod = this.renderInput;
          break;
      }
      const key = Array.isArray(formItem.field)
        ? formItem.field.join("|")
        : formItem.field;

      return (
        <FilterItem
          key={key}
          label={formItem.title}
          size={this.$store.getters.size}
          hideLabel={formItem.type === CheckboxType}
        >
          {renderMethod(formItem)}
        </FilterItem>
      );
    },
    getFormData() {
      const data = cloneDeep(this.formData);
      for (const key in data) {
        if (data.hasOwnProperty(key)) {
          if (Array.isArray(data[key])) {
            data[key] = data[key].join(",");
          }
        }
      }
      return data;
    },
    search() {
      this.$emit("search", this.getFormData());
    },
    reset() {
      this.initFormData();
      this.$emit("reset", this.getFormData());
    },
    setState(data) {
      Object.assign(this.formData, data);
      this.$emit("search", this.getFormData());
    },
    setting() {
      this.generateFilterSettingData();
      this.settingVisable = true;
    },
    generateFields() {
      const {
        sort: sortFields = [],
        hide: hideFields = []
      } = localStore.getFilterSearchConfig(this.id);
      const actualFields = [];
      const formFieldsMap = cloneDeep(this.formFieldsMap);

      sortFields.forEach(key => {
        const item = formFieldsMap[key];
        delete formFieldsMap[key];
        if (item) {
          actualFields.push(item);
        }
      });

      for (const [key, value] of Object.entries(formFieldsMap)) {
        if (!hideFields.includes(key)) {
          actualFields.push(value);
        }
      }

      this.actualFields = actualFields;
    },
    generateFilterSettingData() {
      const { hide: hideFields = [] } = localStore.getFilterSearchConfig(
        this.id
      );
      const formFieldsMap = cloneDeep(this.formFieldsMap);
      const settingData = this.actualFields.map(i => {
        let { field, title } = i;
        if (Array.isArray(field)) {
          field = field.join("|");
        }
        delete formFieldsMap[field];
        return {
          label: title,
          value: field,
          checked: !hideFields.includes(field)
        };
      });
      for (const [key, value] of Object.entries(formFieldsMap)) {
        settingData.push({
          label: value.title,
          value: key,
          checked: false
        });
      }
      this.settingData = settingData;
    },
    saveFilterSetting(filterData) {
      localStore.setFilterSearchConfig(this.id, {
        hide: filterData.filter(i => !i.checked).map(i => i.value),
        sort: filterData.filter(i => i.checked).map(i => i.value)
      });
      this.generateFormFieldMap();
      this.generateFields();
    },
    generateFormFieldMap() {
      const formFieldsMap = {};
      this.formFields.forEach(i => {
        let { field } = i;
        if (Array.isArray(field)) {
          field = field.join("|");
        }
        formFieldsMap[field] = i;
      });
      this.formFieldsMap = formFieldsMap;
    }
  },
  render() {
    /** @type { {visible: boolean, formFields: Array<FormFields>} } */
    const { actualFields, settingVisable, settingData } = this.$data;
    return (
      <div class="SxFilterSearch" id={this.id}>
        <div class="SxFilterSearch__body">
          {actualFields.map(this.renderFormItem)}
          <FilterFooter
            onSearch={this.search}
            onReset={this.reset}
            onSetting={this.setting}
            onStore={this.$store}
            scopedSlots={
              this.$scopedSlots?.footer
                ? {
                    default: () =>
                      this.$scopedSlots?.footer?.({
                        search: this.search,
                        reset: this.reset
                      })
                  }
                : null
            }
          />
        </div>

        <SxFilterFieldSetting
          data={settingData}
          visible={settingVisable}
          title="搜索字段设置"
          on={{
            "update:visible": $event => (this.settingVisable = $event),
            "update-filtered": this.saveFilterSetting
          }}
        />
      </div>
    );
  }
};
</script>

<style scoped lang="scss">
.SxFilterSearch {
  background-color: #fff;
  box-sizing: border-box;
  width: 100% ;
  padding: 10px 20px;

  &__body {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    box-sizing: border-box;
  }

  /deep/ .el-date-editor.el-input {
    width: 100%;
  }
  &__checkbox {
    display: inline-flex;
    flex-direction: row-reverse;
    align-items: center;
    justify-content: flex-end;

    /deep/ .el-checkbox__label {
      font-size: 16px;
      color: #555;
      font-weight: bold;
      margin: 5px 0;
      padding-left: 0;
      padding-right: 10px;
    }
  }
}

.el-date-editor {
  flex: 1;
}
.el-select,
.el-input {
  width: 150px;
}
</style>
